// // Copyright (c) 2009 All Right Reserved // // vl // // 2009-01-01 // Contains ... using System; using System.Diagnostics.Contracts; using System.Globalization; using System.Text; using System.Xml.Serialization; using LargoCommon.Interfaces; using LargoCommon.Localization; namespace LargoCommon.Music { /// Melodic fragment. /// Melodic fragment is a subclass of figural structure. Two types of figures are distinguished: /// modal (tones step within current harmonic modality) and harmonic (tones in actual harmonic structure). /// Melodic fragment is defined by its pattern. In addition to that some characteristics /// (like variability, stability or homogeneity...) are planned to be computed. [Serializable] [XmlRoot] public sealed class MelodicStructure : FiguralSchema, IModalStruct { #region Constructors /// /// Initializes a new instance of the MelodicStructure class. Serializable. public MelodicStructure() { this.ToneSchema = this.SymbolStringOfMode(0); } /// /// Initializes a new instance of the MelodicStructure class. /// /// The given system. /// Structural code. public MelodicStructure(GeneralSystem givenSystem, string structuralCode) : base(givenSystem, structuralCode) { Contract.Requires(givenSystem != null); //// if (givenSystem == null) { return; } //// if (givenSystem.Order == 0) { throw new ArgumentException("Order of MelodicStructure not defined."); } //// HarmonicSystem hs = HarmonicSystem.GetHarmonicSystem(DefaultValue.HarmonicOrder); //// this.HarmonicModality = HarmonicModality.GetNewHarmonicModality(hs, 2741); } /// /// Initializes a new instance of the MelodicStructure class. /// /// The given system. /// Number of instance. public MelodicStructure(GeneralSystem givenSystem, decimal number) : base(givenSystem, number) { Contract.Requires(givenSystem != null); } /// Initializes a new instance of the MelodicStructure class. /// Figural structure. public MelodicStructure(FiguralStructure structure) : base(structure) { Contract.Requires(structure != null); } #endregion #region Interface - simple properties /// Gets or sets the starting drift of the structure. /// Property description. public int Drift { get; set; } /// Gets or sets the starting octave of the structure. /// Property description. public MusicalOctave Octave { get; set; } /// Gets or sets the starting band type of the structure. /// Property description. public MusicalBand BandType { get; set; } /// Gets or sets the starting guessed part type of the structure. /// Property description. public MelodicFunction MelodicFunction { get; set; } /// /// Gets or sets the bar number. /// /// /// The bar number. /// public int BarNumber { get; set; } #endregion #region Interface - object properties /// Gets harmonic system. /// Property description. [XmlIgnore] public MelodicSystem MelodicSystem => (MelodicSystem)this.GSystem; /// Gets or sets harmonic modality. /// Property description. public HarmonicModality HarmonicModality { get => (HarmonicModality)this.Modality; set => this.Modality = value; } /// Gets schema of tones. /// Property description. [XmlAttribute] public string ToneSchema { get; private set; } /// Gets direction. /// Property description. public float Direction { get; private set; } #endregion #region Properties /// /// Gets MelodicTypeString. /// /// Property description. public string MelodicTypeString => LocalizedMusic.String("MelodicFunction" + ((byte)this.MelodicFunction).ToString(CultureInfo.CurrentCulture)); /// /// Gets BandTypeString. /// /// Property description. public string OctaveString => LocalizedMusic.String("Octave" + ((byte)this.Octave).ToString(CultureInfo.CurrentCulture)); #endregion #region Static factory methods /// /// Get New Melodic Struct. /// /// The given system. /// Structural code. /// /// Returns value. /// public static MelodicStructure GetNewMelStruct(GeneralSystem givenSystem, string structuralCode) { Contract.Requires(givenSystem != null); var ms = new MelodicStructure(givenSystem, structuralCode); ms.DetermineBehavior(); return ms; } #endregion #region Comparison /// Support sorting according to level and number. /// Object to be compared. /// Returns value. public override int CompareTo(object obj) { return obj is MelodicStructure ms ? string.Compare(this.PositiveElementSchema, ms.PositiveElementSchema, StringComparison.Ordinal) : 0; //// This kills the DataGrid //// throw new ArgumentException("Object is not a MelodicStructure"); } /// Test of equality. /// Object to be compared. /// Returns value. public override bool Equals(object obj) { //// check null (this pointer is never null in C# methods) if (object.ReferenceEquals(obj, null)) { return false; } if (object.ReferenceEquals(this, obj)) { return true; } if (this.GetType() != obj.GetType()) { return false; } return this.CompareTo(obj) == 0; } #endregion #region Public methods /// /// Get Hash Code. /// /// Returns value. public override int GetHashCode() { const byte hashBase = 17; const byte hashQuotient = 23; unchecked { int result = hashBase; result = (result * hashQuotient) + (this.ToneSchema != null ? this.ToneSchema.GetHashCode() : 0); result = (result * hashQuotient) + this.Drift.GetHashCode(); result = (result * hashQuotient) + this.Octave.GetHashCode(); result = (result * hashQuotient) + this.BandType.GetHashCode(); result = (result * hashQuotient) + this.MelodicFunction.GetHashCode(); result = (result * hashQuotient) + this.Direction.GetHashCode(); return result; } } /// Evaluate properties of the structure. Used in descendant objects. /// Must be virtual, because of call from StructuralVariety. public override void DetermineBehavior() { this.SetElementsFromDifferences(); } /// Test of emptiness. /// Returns value. public override bool IsEmptyStruct() { return false; } /// Validity test. /// Returns value. public override bool IsValidStruct() { if (this.ElementList.Count <= 0) { return true; } int sum = this.ElementList[this.ElementList.Count - 1]; Contract.Assume(sum > short.MinValue); //// Only fragments not exceeding 10 modality steps (c. 1-2 octaves) var ok = Math.Abs(sum) <= 10; return ok; } /// Determine and sets the direction property. public void ComputeDirection() { Contract.Requires(this.ElementList.Count > 0); short firstElem = (byte)this.ElementList[0]; short lastElem = (byte)this.ElementList[this.ElementList.Count - 1]; this.Direction = lastElem - firstElem; } /// /// Complete FromElements. /// public override void CompleteFromElements() { //// Contract.Requires(0 < this.ElementList.Count); if (this.ElementList.Count > 0) { base.CompleteFromElements(); } if (this.ElementList.Count > 0) { this.ComputeDirection(); } } /// /// Planned altitude. /// /// The harmonic system. /// The harmonic modality. /// Index of the modality. /// Returns value. public int PlannedAltitude(GeneralSystem harmonicSystem, BinarySchema harmonicModality, int modalityIndex) { var elements = this.ElementList; if (harmonicSystem == null) { return 0; } if (harmonicModality == null) { return 0; } if (elements.Count <= 0) { return 0; } while (modalityIndex < 0) { modalityIndex += elements.Count; } int index = elements[modalityIndex % elements.Count]; int octaveShift = modalityIndex / harmonicModality.Level; var altitude = (byte)(this.Octave + octaveShift) * harmonicSystem.Order; if (harmonicModality.Level <= 0) { return altitude; } index = index % harmonicModality.Level; if (index >= 0) { altitude += harmonicModality.Places[index]; } return altitude; } #endregion #region String representation /// /// Make string of musical symbols. /// /// Given Shift. /// Returns value. public string SymbolStringOfMode(short givenShift) { var str = new StringBuilder(); var lastE = (byte)(this.GSystem.Order - 1); var hm = (HarmonicModality)this.Modality; if (hm == null) { return string.Empty; } string s; //// = hm.Symbol(givenShift); //// str.Append(s.PadRight(2)); short level; for (byte e = 0; e < lastE; e++) { if (e >= this.ElementList.Count) { continue; } level = (short)(this.ElementList[e] + givenShift); s = hm.SymbolAtLevel(level); if (s != null) { str.Append(s.PadRight(2)); } } if (lastE >= this.ElementList.Count) { return str.ToString(); } level = (short)(this.ElementList[lastE] + givenShift); s = hm.SymbolAtLevel(level); if (s != null) { str.Append(s.PadRight(2)); } return str.ToString(); } /// String representation of the object. /// Returns value. public override string ToString() { // s.Append(this.ElementString()); return string.Format(CultureInfo.InvariantCulture, "{0} {1}", base.ToString(), this.ToneSchema); } #endregion #region Transformations /// /// Makes the descending. /// public void MakeDescending() { var cnt = this.ElementList.Count; for (byte e = 0; e < cnt; e++) { this.ElementList[e] = (short)(cnt - e); } } /// /// Makes the ascending. /// public void MakeAscending() { var cnt = this.ElementList.Count; for (byte e = 0; e < cnt; e++) { this.ElementList[e] = e; } } /// /// Inverts this instance. /// public void Invert() { var cnt = this.ElementList.Count; for (byte e = 0; e < cnt; e++) { this.ElementList[e] = (short)-this.ElementList[e]; } } /// /// Magnifies this instance. /// public void Magnify() { var cnt = this.ElementList.Count; for (byte e = 0; e < cnt; e++) { this.ElementList[e] = (short)(2 * this.ElementList[e]); } } #endregion } }